#include <thread>
#include <giomm/socket.h>
#include <giomm/inetsocketaddress.h>
#include <giomm/memoryinputstream.h>
#include <list>


#include "ServiceAppmanager.h"
#include "ServiceConnection.h"
#include "ServiceManager.h"
#include "App.h"
#include "MessageHelper.h"
#include "Global.h"

#include "json.hpp"

namespace Gnodroid {

    ServiceAppmanager::ServiceAppmanager() :
        Service(SERVICE_APPMANAGER)
    {}

    void
    ServiceAppmanager::start()
    {}

    void
    ServiceAppmanager::stop()
    {}

    void
    ServiceAppmanager::getAppList(sigc::slot<void, bool,
                        std::shared_ptr<std::list<AndroidApp>>/*user*/,
                        std::shared_ptr<std::list<AndroidApp>>/*system*/ > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getService(SERVICE_CONNECTION));
        if (!serviceConnection->isConnecting()) {
            slotCb(false, nullptr, nullptr);
            LOG_ERROR("device not connected");
            return;
        }

        std::thread t (sigc::mem_fun(*this, &ServiceAppmanager::__getAppListThread), slotCb);
        t.detach();
        return;
    }

    void
    ServiceAppmanager::getAppIcons(std::shared_ptr<std::list<AndroidApp>> appList,
                sigc::slot<void, bool, std::shared_ptr<std::list<AndroidApp>> > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getService(SERVICE_CONNECTION));

        if (!serviceConnection->isConnecting()) {
            slotCb(false, nullptr);
            return;
        }

        std::thread t(sigc::mem_fun(*this, &ServiceAppmanager::__getAppIconsThread),
                      appList, slotCb);
        t.detach();
        return;
    }

    void
    ServiceAppmanager::__getAppListThread(sigc::slot<void, bool,
                       std::shared_ptr<std::list<AndroidApp>>/*user*/,
                       std::shared_ptr<std::list<AndroidApp>>/*system*/ > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getService(SERVICE_CONNECTION));

        auto mainContext = App::getCurrentApp()->getMainContext();
        auto socket = Gio::Socket::create(Gio::SOCKET_FAMILY_IPV4,
                                          Gio::SOCKET_TYPE_STREAM,
                                          Gio::SOCKET_PROTOCOL_TCP);
        socket->set_timeout(5);

        try {
            socket->connect(Gio::InetSocketAddress::create(Gio::InetAddress::create("127.0.0.1"),
                                                           serviceConnection->getPcPort()));
        } catch(...) {
            mainContext->invoke([slotCb]()->bool {
                slotCb(false, nullptr, nullptr);
                return false;
            });
            return;
        }

        char* mes = R"#({"service": "Appmanager",
                         "method": "GetAppList"})#";
        if (!MessageHelper::sendMessage(mes, std::strlen(mes)+1, socket)) {
            mainContext->invoke([slotCb]()->bool {slotCb(false, nullptr, nullptr); return false;});
            socket->close();
            return;
        }

        char* recMes = nullptr;
        gssize recLen = 0;

        if (!MessageHelper::receiveMessage(recMes, recLen, socket)) {
            mainContext->invoke([slotCb]()->bool {slotCb(false, nullptr, nullptr); return false;});
            socket->close();
            return;
        }

        //LOG(recMes);

        nlohmann::json recJson;
        try {
            recJson = nlohmann::json::parse(recMes);
        } catch (...) {
            mainContext->invoke([slotCb]()->bool {slotCb(false, nullptr, nullptr); return false;});
            socket->close();
            delete [] recMes;
            return;
        }

        if (recMes) {
            delete [] recMes;
            recMes = nullptr;
        }

        auto userApps = std::shared_ptr<std::list<AndroidApp>>(new std::list<AndroidApp>());
        auto sysApps = std::shared_ptr<std::list<AndroidApp>>(new std::list<AndroidApp>());

        AndroidApp tApp;

        for (auto eachAppUser : recJson["app_user"]) {
            //LOG(eachAppUser["label"]);
            //LOG(eachAppUser["package_name"]);
            tApp.label = eachAppUser["label"];
            tApp.package = eachAppUser["package_name"];
            tApp.version = eachAppUser["version"];
            userApps->push_back(tApp);

        }

        for (auto eachAppSys : recJson["app_system"]) {
            //LOG(eachAppSys["label"]);
            //LOG(eachAppSys["package_name"]);
            tApp.label = eachAppSys["label"];
            tApp.package = eachAppSys["package_name"];
            tApp.version = eachAppSys["version"];
            sysApps->push_back(tApp);
        }

        mainContext->invoke([slotCb, userApps, sysApps]()->bool {
                                slotCb(true, userApps, sysApps);
                                return false;
                            });
        return;

    }

    void
    ServiceAppmanager::__getAppIconsThread(std::shared_ptr<std::list<AndroidApp>> appList,
                        sigc::slot<void, bool, std::shared_ptr<std::list<AndroidApp>> > slotCb)
    {
        ServiceConnection* serviceConnection = static_cast<ServiceConnection*>(App::getService(SERVICE_CONNECTION));
        auto mainContext = App::getCurrentApp()->getMainContext();

        std::shared_ptr<std::list<AndroidApp>> recIcons = std::shared_ptr<std::list<AndroidApp>>(new std::list<AndroidApp>);

        for (AndroidApp app : *appList) {
            std::string mes = std::string() + R"#({"service": "Appmanager",
                                              "method": "GetAppIcon",
                                              "package_name": ")#" + app.package + R"#("})#";

            //LOG("GET ICON MESSAGE: " + mes);

            auto socket = Gio::Socket::create(Gio::SOCKET_FAMILY_IPV4,
                                              Gio::SOCKET_TYPE_STREAM,
                                              Gio::SOCKET_PROTOCOL_TCP);
            socket->set_timeout(5);

            try {
                socket->connect(Gio::InetSocketAddress::create(Gio::InetAddress::create("127.0.0.1"),
                                                               serviceConnection->getPcPort()));
            } catch(...) {
                mainContext->invoke([slotCb]()->bool {
                                        slotCb(false, nullptr);
                                        return false;
                                    });
                return;
            }

            if (!MessageHelper::sendMessage(mes.data(), std::strlen(mes.data())+1, socket)) {
                mainContext->invoke([slotCb]()->bool {slotCb(false, nullptr); return false;});
                socket->close();
                return;
            }

            char* recMes = nullptr;
            gssize recLen = 0;

            if (!MessageHelper::receiveMessage(recMes, recLen, socket)) {
                mainContext->invoke([slotCb]()->bool {slotCb(false, nullptr); return false;});
                socket->close();
                return;
            }

            //LOG(recMes);

            nlohmann::json recJson;
            try {
                recJson = nlohmann::json::parse(recMes);
            } catch(...) {
                mainContext->invoke([slotCb]()->bool {slotCb(false, nullptr); return false;});
                socket->close();
                delete [] recMes;
                return;
            }
            //check status
            if (recJson["status"] == 0) {
                LOG_WARN("failed to get app icon");
                mainContext->invoke([slotCb]()->bool{
                                        slotCb(false, nullptr);
                                        return false;
                                    });
                socket->close();
                delete [] recMes;
                return;
            }

            auto memoryInputStream = Gio::MemoryInputStream::create();
            memoryInputStream->add_data(recMes + std::strlen(recMes) + 1, recLen - std::strlen(recMes) - 1);
            auto icon = Gdk::Pixbuf::create_from_stream(memoryInputStream);
            app.icon = icon;
            recIcons->push_back(app);

            //每5个图标刷新一次
            if (recIcons->size() >= 5) {
                mainContext->invoke([recIcons, slotCb]()->bool {
                                        slotCb(true, recIcons);
                                        return false;
                                    });
                recIcons = std::shared_ptr<std::list<AndroidApp>>(new std::list<AndroidApp>());
            }

            delete [] recMes;

        } //for

        if (recIcons->size() != 0) {
            mainContext->invoke([recIcons, slotCb]()->bool {
                                    slotCb(true, recIcons);
                                    return false;
                                });
        }
        LOG("rec all icons");

        return;
    }
}
